import { readFile, writeFile } from 'fs/promises'
import { resolve } from 'path'

import EventRoute from '../EventRoute'
import Puppeteer from '@main/pupeeter'

const routes: EventRoute[] = []

routes.push(
  new EventRoute('get-dicts', async ({ paths, emitToRenderer }, { id }) => {
    const dictPath = resolve(paths.utils, 'dictList.js')
    const content = await readFile(dictPath, 'utf-8')

    emitToRenderer({ name: 'get-dicts-done', content, id })
  })
)

const puppeteer = new Puppeteer()
type SelectorFun = <T>(selector: string, map: (nodes: Element[]) => T) => Promise<T>

routes.push(
  new EventRoute('puppeteer', async ({ emitToRenderer }, { url, id }) => {
    const page = await puppeteer.gotoUtilIdel(url)

    const iframe = page.frames().find((frame) => frame.name() === 'mainFrame')!

    // 查询表单项
    const collector = await iframe.$$eval('#base > .ax_default', collect)

    emitToRenderer({ name: 'puppeteer-done', id, collector })
  })
)

// $$eval 应该内部使用 eval 执行该段代码，因为 console.log 并不会输出在终端上
const collect = (nodes: Element[]) => {
  const collector = {
    query: [] as any[],
    button: [] as any[],
    table: [] as any[]
  }

  // 获取所有 switchGroup, 用于比对 table-cell 类型
  // existLefts 用于收集 switchGroup 的data-left, 英文同列 switch left相同，但是应该忽略
  const existSwtichLefts = new Set<string>()
  const switches = [] as { left: number; top: number }[]
  const existImageLefts = new Set<number>()
  const images = [] as { left: number }[]

  nodes.forEach((n) => {
    const node = n as HTMLDivElement
    const prev = node.previousElementSibling
    const next = node.nextElementSibling
    const contains = node.classList.contains.bind(node.classList)

    if (
      /** 文本框 */
      contains('text_field') &&
      !prev?.classList.contains('text_field') &&
      !next?.classList.contains('text_field')
    ) {
      collector['query'].push({
        type: 'input',
        label: prev?.textContent?.trim()
      })
    } /** 下拉框 */ else if (contains('droplist')) {
      // 下拉框
      const optionTexts = [...node.querySelectorAll('option')]
        .map((option) => {
          return option.textContent?.trim()
        })
        .filter((text) => text !== '全部')

      const label = node.previousElementSibling?.textContent?.trim()

      collector['query'].push({
        type: 'select',
        label,
        dictLabels: optionTexts
      })
    } /** 操作栏按钮 */ else if (
      (contains('primary_button') || contains('button')) &&
      !['查询', '重置'].includes(node.textContent!.trim())
    ) {
      // 操作栏按钮列表
      const label = node.textContent?.trim()
      collector['button'].push({ label })
    } /** 列表 */ else if (
      node.classList.length === 1 &&
      node.classList[0] === 'ax_default' &&
      !Object.keys(node.dataset).length
    ) {
      /**
       * @description 列表, 通过元素的 offsetLeft 来确定一行的数量
                      页面中包含多个 ax_default 元素，但表示 table 元素的不包含 dataset
       * @description 使用 queueMicrotask 使得在同步时先收集 swtich 元素数据和列表一行的长度
       * 识别列表项的类型（文字、图片、按钮）
       * 1. 图片 => node 下的 div 的 style 设置了 visibility: hidden, display: none
       * 2. switch => 对应的 node 并未包含相关信息，但在顶层，包含 data-label=SwitchGroup 的元素
       *   同时获取 dataset 中的 left / top, 看是否包含在对应的 table-cell 的 DOMRect (left-right, top-bottom) 之间
       * 3. button => 查询是否包含 span.style.text-decoration = underline
       * 4. text => 不符合以上情况
       */
      const queue = collector['table'] as { type: string; label: string }[]
      const tableItems = node.children
      const rowItemsRect = [] as DOMRect[]

      for (let index = 0; index < tableItems.length; index++) {
        const item = tableItems[index] as HTMLDivElement

        if (!item.offsetLeft && index > 0) break

        queue.push({ label: item.textContent!.trim(), type: '' })

        rowItemsRect.push(item.getBoundingClientRect())
      }

      const leftBound = rowItemsRect[0].left

      queueMicrotask(() => {
        const secondRow = [...tableItems].slice(queue.length, queue.length * 2)

        secondRow.forEach((n, firstRowIndex) => {
          const node = n as HTMLDivElement
          const cell = node.lastElementChild as HTMLDivElement
          const cellRect = rowItemsRect[firstRowIndex]
          const textWrapper = node.querySelector('span')

          if (textWrapper && textWrapper.innerText) {
            if (textWrapper.style.textDecoration === 'underline') {
              queue[firstRowIndex].type = 'button'
            } else {
              queue[firstRowIndex].type = 'text'
            }
          } else if (cell.style.display === 'none') {
            // 图片 或 switch
            const { left: cellLeft, right: cellRight } = cellRect
            const isMatchSwitch = switches.some((v) => v.left > cellLeft && v.left < cellRight)
            const isMatchImage = images.some(
              (v) => v.left - leftBound > cellLeft && v.left - leftBound < cellRight
            )
            if (isMatchSwitch) {
              queue[firstRowIndex].type = 'switch'
            } else if (isMatchImage) {
              queue[firstRowIndex].type = 'image'
            } else {
              queue[firstRowIndex].type = 'text'
            }
          }
        })
      })
    } /** 列表的 switch 按钮 */ else if (node.dataset.label === 'SwitchGroup') {
      const { left, top } = node.dataset

      if (!existSwtichLefts.has(left!)) {
        switches.push({
          left: Number(left),
          top: Number(top)
        })
      }

      existSwtichLefts.add(left!)
    } /** 列表的 image 图片 */ else if (contains('image')) {
      const { left } = node.getBoundingClientRect()

      if (!existImageLefts.has(left)) {
        images.push({ left })
      }

      existImageLefts.add(left)
    } else {
      /** 时间选择器 */
      const containKeyword = node.textContent?.trim() === '至'
      const hasShapeFlag = Boolean(node.previousElementSibling?.querySelector('input'))

      if (containKeyword && hasShapeFlag) {
        collector['query'].push({ type: 'range', label: '选择时间' })
      }
    }

    return false
  })

  return collector
}

export default routes.map((route) => route.setType('tool'))
